import os
import json
import logging
import httpx
from typing import List, Dict, Any, Optional, Union

from app.utils.vectorstore_client import VectorStoreClient, Document
from app.core.config import settings

# 创建logger对象
logger = logging.getLogger(__name__)

# 导入LangChain
try:
    from langchain_openai import ChatOpenAI
    from langchain_core.messages import SystemMessage, HumanMessage
    LANGCHAIN_AVAILABLE = True
except ImportError:
    logger.warning("无法导入LangChain，将使用httpx进行API调用")
    LANGCHAIN_AVAILABLE = False

class RAGService:
    """RAG服务类，实现基于向量存储的知识检索和问答功能"""
    
    def __init__(self):
        self.vector_store_client = VectorStoreClient.get_instance()
        self.llm_api_url = settings.LLM_API_URL
        self.llm = None
        
        # 初始化LLM
        if LANGCHAIN_AVAILABLE:
            try:
                self.llm = ChatOpenAI(
                    temperature=0.5,
                    model_name="qwen-turbo",
                    openai_api_key="sk-62ac5519d1ad4e36abe7c47c3a43e0bc",
                    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
                    streaming=False,
                    openai_proxy=None
                )
                logger.info("成功初始化LangChain ChatOpenAI")
            except Exception as e:
                logger.error(f"初始化LangChain ChatOpenAI失败: {str(e)}")
                self.llm = None
        
    @staticmethod
    async def answer_question(
                             query: str, 
                             context: Optional[str] = None,
                             max_tokens: int = 2048) -> Dict[str, Any]:
        """
        回答问题
        
        Args:
            query: 用户问题
            context: 额外上下文
            max_tokens: 最大输出token数
            
        Returns:
            回答结果
        """
        service = RAGService()
        
        # 检查向量存储是否初始化
        init_error = VectorStoreClient.get_instance().get_init_error()
        if init_error:
            return {
                "success": False,
                "message": f"向量存储未正确初始化: {init_error}",
                "answer": "系统无法回答您的问题，因为向量存储未正确初始化。请联系管理员检查系统配置。"
            }
            
        try:
            # 获取相关文档
            search_result = await VectorStoreClient.get_instance().similarity_search(query)
            
            # 适配新的返回值格式
            if isinstance(search_result, dict):
                success = search_result.get("success", False)
                docs = search_result.get("documents", [])
                error_msg = search_result.get("error", "未知错误")
                
                if not success:
                    logger.error(f"向量存储搜索失败: {error_msg}")
                    return {
                        "success": False,
                        "message": f"向量存储搜索失败: {error_msg}",
                        "answer": "对不起，在搜索相关内容时出现了问题。请稍后再试或联系系统管理员。"
                    }
            else:
                # 兼容旧格式，直接返回文档列表
                docs = search_result
            
            # 检查有无上下文
            has_context = context is not None and len(context.strip()) > 0
            
            # 处理docs为None或空列表，但有上下文的情况
            if not docs and not has_context:
                return {
                    "success": False,
                    "message": "未找到相关文档，请尝试其他问题或添加更多知识库文档。",
                    "answer": "我无法回答这个问题，因为没有找到相关的知识库内容。请尝试重新表述您的问题，或者联系管理员添加更多相关文档。"
                }
            
            # 构建文档上下文
            doc_contexts = []
            if docs:
                for i, doc in enumerate(docs):
                    content = doc.page_content.strip()
                    metadata = doc.metadata or {}
                    source_info = f" (来源: {metadata.get('source', '未知来源')})" if metadata.get('source') else ""
                    doc_contexts.append(f"文档 {i+1}{source_info}:\n{content}")
            
            # 合并所有上下文
            context_sections = []
            
            # 添加文档上下文
            if doc_contexts:
                doc_context_text = "\n\n".join(doc_contexts)
                context_sections.append("【检索文档内容】\n" + doc_context_text)
            
            # 添加自定义上下文（如产品信息）
            if has_context:
                context_sections.append("【产品信息】\n" + context.strip())
            
            # 合并所有上下文
            combined_context = "\n\n" + "\n\n".join(context_sections) if context_sections else ""
            
            # 日志记录
            logger.info(f"用户查询: {query}")
            logger.info(f"查询上下文: 检索到{len(docs) if docs else 0}个文档, 额外上下文长度: {len(context) if has_context else 0}")
            
            # 改进系统提示词，增强对产品YAML数据的处理能力
            system_prompt = """你是一个专业的产品分析助手，专门帮助用户解答产品相关问题。

请按照以下指南工作：

1. 当提供了产品数据（YAML格式）时，这是你的首要参考信息源。每个产品都有明确的标记，请注意区分不同产品的数据。

2. YAML格式数据包含了产品的全部属性，可能包括：
   - 基本信息：名称、ID等
   - 分析结果：技术参数、优势、特点等
   - 图片文本：从产品图片中提取的文本内容
   - 其他元数据

3. 回答用户问题时，应该：
   - 基于提供的YAML数据给出专业、精确的回答
   - 明确指出信息来源自哪个产品
   - 如果涉及多个产品，可以进行对比分析
   - 当数据中没有足够信息时，坦诚告知用户

4. 保持专业、客观的回答风格，避免生成虚构的信息。如果用户提问的内容超出数据范围，可以基于通用知识回答，但请明确指出哪些是来自数据，哪些是基于通用知识。
"""

            # 用户提示词包含检索到的内容和问题
            user_prompt = f"""以下是与您的问题相关的内容：
{combined_context}

请根据上述内容，回答我的问题: {query}"""

            # 调用LLM API
            result = await service._make_api_request(system_prompt, user_prompt, max_tokens)
            
            # 构建返回结果
            return_docs = []
            if docs:
                return_docs = [{"content": doc.page_content, "metadata": doc.metadata} for doc in docs]
                
            return {
                "success": True,
                "message": "成功回答问题",
                "answer": result,
                "sources": return_docs  # 改名为sources更符合前端使用
            }
            
        except Exception as e:
            logger.error(f"回答问题时出错: {str(e)}")
            return {
                "success": False,
                "message": f"处理问题时出错: {str(e)}",
                "answer": "抱歉，在处理您的问题时发生了错误。请稍后再试或联系支持人员。"
            }
    
    async def analyze_product_comparison(self, product_data: List[Dict], max_tokens: int = 2048, question: Optional[str] = None) -> Dict[str, Any]:
        """
        分析并对比多个产品的信息
        
        Args:
            product_data: 产品数据列表
            max_tokens: 最大生成token数
            question: 可选的自定义问题
            
        Returns:
            对比分析结果
        """
        try:
            if not product_data or len(product_data) < 1:
                return {
                    "error": "至少需要选择一个产品进行分析",
                    "result": "请至少选择一个已完成AI分析的产品。"
                }
                
            product_info = []
            
            for product in product_data:
                product_id = product["id"]
                product_name = product["name"]
                extracted_text = product["extracted_text"]
                analysis_result = product["analysis_result"]
                
                # 检查是否已有分析结果
                if analysis_result and isinstance(analysis_result, dict) and len(analysis_result) > 0:
                    product_info.append({
                        "id": product_id,
                        "name": product_name,
                        "analysis": analysis_result
                    })
                else:
                    logger.warning(f"产品 {product_id} 没有分析结果，无法进行对比")
            
            if not product_info:
                return {
                    "error": "没有可用的产品分析结果",
                    "result": "所选产品没有完成AI分析，无法进行对比。请先进行产品分析。"
                }
            
            # 构建提示词
            product_count = len(product_info)
            system_prompt = f"""
你是一个专业的产品分析师，现在需要对{product_count}个产品进行分析对比。
请基于提供的每个产品的AI分析结果，生成一个结构化的对比分析报告。
分析应该重点关注以下方面：
1. 主要功能特点对比
2. 技术参数对比
3. 市场优势对比
4. 竞品分析
5. 核心亮点总结

请按照上述五个部分进行分析，每个部分需要对所有产品进行横向比较。
分析应当客观公正，基于事实，突出各产品的优缺点。
"""

            user_prompt = "以下是产品的分析信息：\n\n"
            for idx, product in enumerate(product_info):
                user_prompt += f"产品{idx+1}: {product['name']}\n"
                analysis = product['analysis']
                for key, value in analysis.items():
                    if isinstance(value, str) and value.strip():
                        user_prompt += f"{key}: {value}\n"
                user_prompt += "\n---\n\n"

            # 如果有自定义问题，使用自定义问题
            if question:
                user_prompt += f"\n{question}"
            else:
                user_prompt += "\n请提供这些产品的对比分析报告，按照前面提到的5个方面进行分析。"
            
            # 调用API获取回答
            response = await self._make_api_request(system_prompt, user_prompt, max_tokens, temperature=0.3)
            
            if not response:
                return {
                    "error": "无法获取AI分析结果",
                    "result": "产品对比分析失败，请稍后再试。"
                }
            
            # 处理结果
            return {
                "product_ids": [p["id"] for p in product_info],
                "product_names": [p["name"] for p in product_info],
                "result": response
            }
        
        except Exception as e:
            logger.error(f"产品对比分析异常: {str(e)}")
            return {
                "error": str(e),
                "result": "产品对比分析过程中发生错误，请稍后再试。"
            }

    async def answer_document_question(self, document_path: str, question: str) -> Dict[str, Any]:
        """
        根据上传的文档回答问题
        
        Args:
            document_path: 文档路径
            question: 用户问题
            
        Returns:
            包含回答和来源的字典
        """
        try:
            # 检查向量存储是否已初始化
            if not self.vector_store_client.is_initialized:
                error_msg = f"AI助手无法运行: 向量存储未初始化"
                logger.error(error_msg)
                return {
                    "answer": f"抱歉，我目前无法回答关于这个文档的问题。系统管理员需要安装必要的依赖项来启用完整功能。",
                    "sources": []
                }
            
            # 加载文档
            from app.utils.document_loader import load_document
            document = await load_document(document_path)
            if not document:
                return {
                    "answer": "抱歉，无法读取此文档。可能是文档格式不受支持或文档已损坏。",
                    "sources": []
                }
            
            # 分割文档
            from app.utils.document_splitter import split_document
            chunks = await split_document(document)
            if not chunks:
                return {
                    "answer": "抱歉，无法处理此文档内容。文档可能为空或格式不兼容。",
                    "sources": []
                }
            
            # 创建临时向量存储
            import tempfile
            import hashlib
            
            # 为文档创建唯一标识符
            doc_hash = hashlib.md5(document_path.encode()).hexdigest()
            temp_store_path = os.path.join("documents", f"temp_{doc_hash}")
            
            # 使用更新后的向量存储客户端创建临时向量存储
            success, error_msg = await self.vector_store_client.create_or_load_vectorstore(
                documents=chunks,
                store_path=temp_store_path,
                store_type="chroma"
            )
            
            if not success:
                logger.error(f"创建文档向量存储失败: {error_msg}")
                return {
                    "answer": f"抱歉，无法创建向量存储来处理您的文档查询。\n\n原因: {error_msg}",
                    "sources": []
                }
            
            # 从特定的向量存储路径检索相关内容
            search_result = await self.vector_store_client.similarity_search(
                query=question, 
                k=5,
                store_path=temp_store_path
            )
            
            # 适配新的返回值格式
            if isinstance(search_result, dict):
                success = search_result.get("success", False)
                retrieved_docs = search_result.get("documents", [])
                error_msg = search_result.get("error", "未知错误")
                
                if not success:
                    logger.error(f"文档向量存储搜索失败: {error_msg}")
                    return {
                        "answer": f"抱歉，在搜索文档内容时出现了问题。\n\n原因: {error_msg}",
                        "sources": []
                    }
            else:
                # 兼容旧格式，直接使用文档列表
                retrieved_docs = search_result
            
            if not retrieved_docs:
                return {
                    "answer": "抱歉，在文档中没有找到与您问题相关的内容。请尝试换一种方式提问或上传包含相关信息的文档。",
                    "sources": []
                }
            
            # 构建上下文
            contexts = [doc.page_content for doc in retrieved_docs]
            context_text = "\n\n".join(contexts)
            
            # 构建提示
            system_prompt = """你是一个专业的文档分析助手。请根据以下文档内容回答用户问题。
如果文档内容中没有相关信息，请说"文档中没有相关内容"。"""
            
            user_prompt = f"""
文档内容:
{context_text}

用户问题: {question}
"""
            
            # 调用LLM API
            answer = await self._make_api_request(system_prompt, user_prompt, 1500, 0.2)
            
            if not answer:
                return {
                    "answer": "抱歉，无法从文档中获取答案。请稍后再试或使用不同的问题。",
                    "sources": []
                }
            
            return {
                "answer": answer,
                "sources": [{"content": doc.page_content, "metadata": doc.metadata} for doc in retrieved_docs]
            }
        
        except Exception as e:
            logger.error(f"处理文档查询失败: {str(e)}")
            return {
                "answer": f"抱歉，处理您的问题时出现了错误。请稍后再试或尝试重新上传文档。\n\n错误详情: {str(e)}",
                "sources": []
            }

    async def _make_api_request(self, system_prompt: str, user_prompt: str, max_tokens: int = 2048, temperature: float = 0.7) -> Optional[str]:
        """
        向LLM API发送请求并获取回答
        
        Args:
            system_prompt: 系统提示词
            user_prompt: 用户提示词
            max_tokens: 最大生成token数
            temperature: 温度参数，控制随机性
            
        Returns:
            生成的文本内容，如果请求失败返回None
        """
        # 构建完整提示文本
        if not user_prompt:
            full_prompt = system_prompt
        else:
            full_prompt = f"{system_prompt}\n\n{user_prompt}"
            
        logger.info(f"正在向LLM发送请求，提示词长度: {len(full_prompt)}")
        
        # 首先尝试使用LangChain
        if LANGCHAIN_AVAILABLE and self.llm is not None:
            try:
                # 构建消息
                messages = [
                    SystemMessage(content=system_prompt),
                    HumanMessage(content=user_prompt if user_prompt else "")
                ]
                
                # 调用LLM
                response = await self.llm.agenerate([messages])
                
                # 获取生成的内容
                if response and response.generations and response.generations[0]:
                    return response.generations[0][0].text
                else:
                    logger.warning("LLM返回空响应")
                    return self._get_fallback_response("空响应")
            except Exception as e:
                logger.error(f"LangChain调用失败: {str(e)}")
                # 继续尝试使用httpx
        
        # 使用httpx作为备选
        try:
            async with httpx.AsyncClient() as client:
                response = await client.post(
                    f"{self.llm_api_url}/chat/completions",
                    json={
                        "model": "qwen-72b",
                        "messages": [
                            {"role": "system", "content": system_prompt},
                            {"role": "user", "content": user_prompt if user_prompt else ""}
                        ],
                        "max_tokens": max_tokens,
                        "temperature": temperature
                    },
                    timeout=60.0  # 增加超时时间，因为对比分析可能需要更长时间
                )
                
                if response.status_code == 200:
                    result = response.json()
                    return result.get("choices", [{}])[0].get("message", {}).get("content", "")
                else:
                    logger.error(f"LLM API调用失败: {response.status_code} - {response.text}")
                    return self._get_fallback_response("API错误")
        except Exception as e:
            logger.error(f"LLM API请求异常: {str(e)}")
            return self._get_fallback_response("请求异常")
            
    def _get_fallback_response(self, error_type: str) -> str:
        """
        在API不可用时提供回退响应
        
        Args:
            error_type: 错误类型
            
        Returns:
            回退响应文本
        """
        return f"""抱歉，AI服务暂时不可用（{error_type}）。

系统无法处理您的请求，可能的原因：
1. AI服务连接问题
2. 服务暂时不可用

请稍后再试或联系系统管理员。""" 